#include "pic16e_reg.h"
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <time.h>
#include <vector>
extern "C" {
#include "../common.h"
#include "../yy.h"
}
#include "../sizer.h"
#include "../pcoder.h"
#include "../display.h"
#include "pic16e.h"
#include "pic16e_asm.h"
#include "pic16e_inst.h"

static int totalParSize;
static int totalTmpSize;
static int totalVarSize;

static int totalParCount;
static int totalTmpCount;
static int totalVarCount;

static void tmpCollect(Pnode *head);
static int tmpMaxSize(Pnode *head, int index);
static int listData(Fnode *fp, Item **list, Pnode *pnp);


/*
main:
        addi    sp,sp,-32
        sw      ra,28(sp)
        sw      s0,24(sp)
        addi    s0,sp,32
=============  func_begain_end =============
        li      a5,1
        sw      a5,-20(s0)
        li      a5,0
        mv      a0,a5
        lw      ra,28(sp)
        lw      s0,24(sp)
        addi    sp,sp,32
        jr      ra
 */
void PIC16E :: funcBeg(Pnode *pnp)
{
	char *fname = curFunc->name;
	int fParCount = curFunc->parCount();
	char argType[256];
	if (fParCount >= 1){
		sprintf(argType,"%s",getNodeTypeStr(curFunc->parAttr(0)));
	}
	for (int i = 1; i < fParCount; i++){
		sprintf(argType,"%s_%s", argType, getNodeTypeStr(curFunc->parAttr(i)));
	}
	char *s = STRBUF();
	if (fParCount >= 1){
		sprintf(s,"%s_%s", fname, argType);
	}else {
		sprintf(s,"%s", fname);
	}
	char buf[256];
	sprintf(buf, "\n\t.zero   4\n"
        "\t.text\n"
        "\t.align  2\n"
        "\t.globl  %s\n"
        "\t.type   %s, @function\n"
	,s,s);
	ASM_OUTP(buf);
	ASM_OUTP(s), ASM_OUTP(":\n");
	Item *list = NULL;
	int data_size =  listData(curFunc, &list, pnp);
	int bit_size = 16 + (((data_size - 1)/ 16) + 1) * 16;
	ASM_CODE(_ADDI,_R_SP, _R_SP, -bit_size);
	ASM_CODE(_SW,_R_RA,indirectAddressing(bit_size - 4, _R_SP));
	ASM_CODE(_SW,_R_S0,indirectAddressing(bit_size - 8, _R_SP));
	sprintf(curFunc->endAsmStr,"\tlw  \tra, %d(sp)\n\tlw  \ts0, %d(sp)\n\taddi\tsp, sp, %d\n",bit_size - 4, bit_size - 8, bit_size);
	ASM_CODE(_ADDI, _R_S0,_R_SP, bit_size);
	
	while (list) {
		ASM_OUTP(list->val.s);	
		Item *tmp = list->next;
		delete list; list = tmp;
	}
	
}
/**
        lw      ra,44(sp)
        lw      s0,40(sp)
        addi    sp,sp,48
 */
void PIC16E :: funcEnd(void)
{
	ASM_OUTP(curFunc->endAsmStr);
	ASM_OUTP("\tret \n");
	//ASM_CODE(_JR,_R_RA);
}

/////////////////////////////////////////////////////////////////////////////////
static void tmpCollect(Fnode *fp ,Pnode *head)
{
		
	int tsize = 0;
	int depth = 0;
	int in[300] = {0};
	Pnode* head_bak = head;
    while ( head && head->type != P_FUNC_END )
    {
		depth ++;
        for (int i = 0; i < 3; i++)
        {
            Item *ip = head->items[i];
            if ( ip && ip->type == TEMP_ITEM )
			{
				in[ip->val.i + 1] = depth;
				if (totalTmpCount < ip->val.i + 1){
					totalTmpCount = ip->val.i + 1;	
				}
				if (totalTmpCount > 6 && ip->val.i > 5){
					tsize += sizer(ip->attr, ATTR_SIZE);
				}
				if (tsize > totalTmpSize){
					totalTmpSize = tsize;
				}
			}
        }
        head = head->next;
    }
	depth = 0;
	head = head_bak;
	regexManager* rg = regexManager::getInstance();
	int baseOffset = 0;
    while ( head && head->type != P_FUNC_END )
    {
		if (head->type == ';'){
			int depth = 0;
			int in[100] = {0};	
			head = head->next;
			Pnode* head_bbak = head;
			while (head && head->type != P_FUNC_END && head->type != ';') {
				depth ++;
				for (int i = 0; i < 3; i++)
				{
					Item *ip = head->items[i];
					if ( ip && ip->type == TEMP_ITEM )
					{
						in[ip->val.i + 1] = depth;
					}
				}
				head = head->next;	
			}
			head = head_bbak;
			depth = 0;
			while (head && head->type != P_FUNC_END && head->type != ';') {
				depth ++;
				for (int i = 0; i < 3; i++)
				{
					Item *ip = head->items[i];
					if ( ip && ip->type == TEMP_ITEM )
					{
						int x = ip->val.i + 1;
						int res = rg->assignRegex(x);
						if (res == -1){baseOffset+=4;ip->attr->offset += baseOffset;}
						else if (res != -2) { ip->memRgx = res; }
						if (in[x] <= depth){
							rg->unAssignRegex(x);
						}
					}
				}
				head = head->next;	
			}
		} else {
			head = head->next;
		}
    }
}



static void addItem(Item **list, Item *ip)
{
	if ( *list == NULL )
		*list = ip;
	else
	{
		Item *ihead = *list;
		while ( ihead->next ) ihead = ihead->next;
		ihead->next = ip;
	}
}

static int listData(Fnode *fp, Item **list, Pnode *pnp)
{
	char buf[1024];

	totalParSize = 0;
	totalVarSize = 0;
	totalTmpSize = 0;

	totalParCount = 0;
	totalVarCount = 0;
	totalTmpCount = 0;
	
	// list all parameter variables
	for (Dnode *dnp = fp->dlink->dlist; dnp; dnp = dnp->next)
	{
		totalParSize += dnp->size();
		totalParCount++;
	}

	// list all non-static variables
	for (int sequence = 0;;)
	{
		int offset, depth = sequence++;
		Dnode *dnp = fp->getData(GENERIC_DATA, &depth, &offset);
		if ( dnp == NULL ) break;
		offset += dnp->size();
		if ( totalVarSize < offset ) totalVarSize = offset;
	}


	// list all temp variables
	tmpCollect(fp,pnp);
	

	int data_size = totalParSize + totalVarSize + totalTmpSize;
	int bit_size = 16 + (((data_size - 1)/ 16) + 1) * 16;
	

	// calcutor var memstr	
	char memStrBuf[64];
	// func args
	int parIdx = 0;
	for (Dnode *dnp = fp->dlink->dlist; dnp; dnp = dnp->next)
	{
		sprintf(buf, "\t%s\ta%d, -%d(s0)\n", _SW, parIdx, bit_size - 8 - (totalParCount - parIdx - 1) * 4 );
		addItem(list, strItem(buf));
		sprintf(memStrBuf, "-%d(s0)", bit_size - 8 - (totalParCount - parIdx - 1) * 4 );
		parIdx++;
		dnp->memStr = dupStr(memStrBuf);
	}
	// func child var	
	for (int sequence = 0;;)
	{
		int offset, depth = sequence++;
		Dnode *dnp = fp->getData(GENERIC_DATA, &depth, &offset);
		if ( dnp == NULL ) break;
		offset += dnp->size();
		if (dnp->attr->dimVect){
			sprintf(memStrBuf, "%d", -20 - offset + 4);
		}else {
			sprintf(memStrBuf, "%d(s0)", -20 - offset + 4);
		}
		dnp->memStr = dupStr(memStrBuf);
	}

	// tmp vars
		
	
	return totalParSize + totalVarSize + totalTmpSize;
}

void PIC16E :: call(char *funcname)
{
	ASM_CODE(_CALL, funcname);
}